package edu.stonybrook.cs.gars.server.data;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import edu.stonybrook.cs.gars.server.data.exceptions.DuplicateFieldException;
import edu.stonybrook.cs.gars.server.data.exceptions.MapFormatException;

/**
 * Contains mapping rules for mapping a data file to a database
 * 
 * @author Tony Arra
 */
public class Rules {
	private ArrayList<Rule> rules = new ArrayList<Rule>();

	/**
	 * Constructs empty set of rules
	 * 
	 * @throws IOException
	 * @throws MapFormatException
	 * @throws DuplicateFieldException 
	 */
	public Rules(File map) throws IOException, MapFormatException, DuplicateFieldException {
		try {
			parse(map);
		} catch (IOException e) {
			throw e;
		} catch (MapFormatException e) {
			throw e;
		} 
	}

	/**
	 * Parses a mapping file and creates a set of mapping rules
	 * 
	 * @param map
	 * @throws IOException
	 * @throws MapFormatException
	 * @throws DuplicateFieldException 
	 */
	private void parse(File map) throws IOException, MapFormatException, DuplicateFieldException {
		int arrowIndex = -1;
		String line;
		String from, to;
		BufferedReader reader = new BufferedReader(new FileReader(map));
		while ((line = reader.readLine()) != null && line.length() > 0) {
			if ((arrowIndex = line.indexOf("->")) != -1) {
				from = line.substring(0, arrowIndex);
				to = line.substring(arrowIndex + 2);
				addRule(from, to);
			} else {
				throw new MapFormatException(map, line);
			}
		}
		//Duplicate fields
		findDuplicate();
	}
	/**
	 * Checks the rules for a duplicate mapping
	 * @throws DuplicateFieldException
	 */
	private void findDuplicate() throws DuplicateFieldException {
		Iterator<Rule> rulesIt = rules.iterator();
		ArrayList<String> toList = new ArrayList<String>();
		String from, duplicateFrom;
		String to;
		int index = -1;
		Rule rule;
		while (rulesIt.hasNext()) {
			rule = rulesIt.next();
			from = rule.getFrom();
			to = rule.getTo();
			index = toList.indexOf(to);
			if (index != -1 && !to.equals("ignore") && !to.equals("otherInfo")) {
				duplicateFrom = rules.get(index).getFrom();
				throw new DuplicateFieldException(duplicateFrom, from, to);
			} else toList.add(to);
		}
	}

	/**
	 * Creates rules iterator
	 * 
	 * @return rules iterator
	 */
	public Iterator<Rule> getRulesIt() {
		return rules.iterator();
	}

	/**
	 * Adds a rule
	 * 
	 * @param from
	 * @param to
	 */
	public void addRule(String from, String to) {
		Rule rule = new Rule(from, to);
		rules.add(rule);
	}

	/**
	 * Represents a single rule
	 * 
	 * @author Tony Arra
	 */
	public class Rule {
		private String from, to;

		/**
		 * Constructs a new rule
		 * 
		 * @param from
		 * @param to
		 */
		public Rule(String from, String to) {
			this.from = from;
			this.to = to;
		}

		/**
		 * Gets field
		 * 
		 * @return field
		 */
		public String getFrom() {
			return from;
		}

		/**
		 * Gets destination
		 * 
		 * @return destination
		 */
		public String getTo() {
			return to;
		}
	}
}
